package com.csii.activiti.server.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.csii.activiti.server.abstraction.entity.ActUser;
import com.csii.activiti.server.abstraction.service.ActBasicService;
import com.csii.activiti.server.abstraction.service.ActUserService;
import com.csii.activiti.server.core.constant.ActFlowActionSettingConstant;
import com.csii.activiti.server.core.constant.ActFlowStatusConstant;
import com.csii.activiti.server.core.constant.CommonConstant;
import com.csii.activiti.server.core.constant.FlowNodeTypeConstant;
import com.csii.activiti.server.core.entity.ActFlowFormContentSetting;
import com.csii.activiti.server.core.entity.ActFlowProIdAndNode;
import com.csii.activiti.server.core.entity.ActFlowTodo;
import com.csii.activiti.server.core.entity.db.ActFlowNodeEntity;
import com.csii.activiti.server.core.entity.db.ActFlowPeopleWhoCopiedEntity;
import com.csii.activiti.server.core.entity.db.ActFlowPublishConfigEntity;
import com.csii.activiti.server.core.mapper.ActExtensionMapper;
import com.csii.activiti.server.core.mapper.ActFlowNodeMapper;
import com.csii.activiti.server.core.mapper.ActFlowPeopleWhoCopiedMapper;
import com.csii.activiti.server.core.mapper.ActFlowPublishConfigurationMapper;
import com.csii.activiti.server.core.query.QueryActFlowPeopleWhoCopied;
import com.csii.activiti.server.core.service.FlowPeopleWhoCopiedService;
import com.csii.activiti.server.core.utils.ELUtils;
import com.csii.activiti.server.core.utils.FormDataPermissionUtils;
import org.activiti.engine.HistoryService;
import org.activiti.engine.history.HistoricVariableInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class FlowPeopleWhoCopiedServiceImpl extends ServiceImpl<ActFlowPeopleWhoCopiedMapper, ActFlowPeopleWhoCopiedEntity> implements FlowPeopleWhoCopiedService {

    @Autowired
    private ActBasicService actBasicService;

    @Autowired
    private ActUserService actUserService;

    @Autowired
    private ActExtensionMapper actExtensionMapper;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private ActFlowNodeMapper actFlowNodeMapper;

    @Autowired
    private ActFlowPublishConfigurationMapper actFlowPublishConfigurationMapper;

    @Override
    public Page<ActFlowPeopleWhoCopiedEntity> queryPeopleWhoCopiedList(QueryActFlowPeopleWhoCopied queryActFlowPeopleWhoCopied, Page page) {
        ActFlowPeopleWhoCopiedMapper baseMapper = getBaseMapper();
        String username = actBasicService.getCurrentLoginUser().getUsername();
        String currentTenantId = actBasicService.getCurrentTenantId();
        LambdaQueryWrapper<ActFlowPeopleWhoCopiedEntity> queryWrapper = new LambdaQueryWrapper<ActFlowPeopleWhoCopiedEntity>().eq(ActFlowPeopleWhoCopiedEntity::getPwcUserName, username).orderByDesc(ActFlowPeopleWhoCopiedEntity::getCreateTime);
        queryWrapper.eq(ActFlowPeopleWhoCopiedEntity::getActTenantId,currentTenantId);
        if (StrUtil.isNotBlank(queryActFlowPeopleWhoCopied.getProcessName())){
            queryWrapper.like(ActFlowPeopleWhoCopiedEntity::getProcessName,queryActFlowPeopleWhoCopied.getProcessName());
        }

        if (StrUtil.isNotBlank(queryActFlowPeopleWhoCopied.getInitiator())){
            queryWrapper.eq(ActFlowPeopleWhoCopiedEntity::getInitiator,queryActFlowPeopleWhoCopied.getInitiator());
        }

        if (queryActFlowPeopleWhoCopied.getStartTime() != null){
            queryWrapper.ge(ActFlowPeopleWhoCopiedEntity::getCreateTime,queryActFlowPeopleWhoCopied.getStartTime());
        }

        if (queryActFlowPeopleWhoCopied.getEndTime() != null){
            queryWrapper.lt(ActFlowPeopleWhoCopiedEntity::getCreateTime,queryActFlowPeopleWhoCopied.getEndTime());
        }

        Page<ActFlowPeopleWhoCopiedEntity> pageList = baseMapper.selectPage(page, queryWrapper);
        List<ActFlowPeopleWhoCopiedEntity> entityList = pageList.getRecords();
        if (CollUtil.isNotEmpty(entityList)) {
            Set<String> deploymentIds = entityList.stream().filter(item->item.getDeploymentId() != null).map(ActFlowPeopleWhoCopiedEntity::getDeploymentId).collect(Collectors.toSet());
            Map<String, ActFlowPublishConfigEntity> deploymentIdAndPublishMap = null;
            if(!deploymentIds.isEmpty()){
                deploymentIdAndPublishMap = queryDeploymentIdAndPublishMap(deploymentIds);
            }
            Set<String> processInstanceIds = entityList.stream().map(ActFlowPeopleWhoCopiedEntity::getProcessInstanceId).collect(Collectors.toSet());
            List<ActFlowProIdAndNode> proIdAndNodeList = queryFlowProIdAndNodeGetGroupMax(processInstanceIds, null, false);
            Set<String> usernames = entityList.stream().map(ActFlowPeopleWhoCopiedEntity::getInitiator).collect(Collectors.toSet());
            if (CollUtil.isNotEmpty(proIdAndNodeList)){
                usernames.addAll(proIdAndNodeList.stream().filter(item -> item.getAssignee() != null).map(ActFlowProIdAndNode::getAssignee).collect(Collectors.toSet()));
            }
            Map<String, String> usernameAndNickname = queryUsernameAndNickname(usernames);

            Map<String, List<ActFlowProIdAndNode>> proInsIdAndNodeList = null;
            if (CollUtil.isNotEmpty(proIdAndNodeList)){
                proInsIdAndNodeList = proIdAndNodeList.stream().collect(Collectors.groupingBy(ActFlowProIdAndNode::getProcessInstanceId, Collectors.mapping(item->{
                    if (!usernameAndNickname.isEmpty() && item.getAssignee() != null){
                        item.setAssignee(usernameAndNickname.get(item.getAssignee()));
                    }
                    return item;
                }, Collectors.toList())));
            }

            Map<String, Map<String, Object>> processInstanceIdAndFormData = null;
            Map<String, List<ActFlowFormContentSetting>> formDataPermissionMap = new HashMap<>();

            processInstanceIdAndFormData = queryVariableByExecutionId(processInstanceIds);
            List<ActFlowNodeEntity> actFlowNodeEntities = actFlowNodeMapper.selectBatchIds(entityList.stream().map(ActFlowPeopleWhoCopiedEntity::getNodeId).collect(Collectors.toSet()));
            if (CollUtil.isNotEmpty(actFlowNodeEntities)){
                formDataPermissionMap.putAll(actFlowNodeEntities.stream().collect(Collectors.toMap(ActFlowNodeEntity::getId, item-> item.transitionActFlowNode().getConfigurationItem().getFormContentSetting(), (v1, v2) -> v2)));
            }

            for (ActFlowPeopleWhoCopiedEntity actFlowPeopleWhoCopied : entityList) {
                String nickname = usernameAndNickname.get(actFlowPeopleWhoCopied.getInitiator());
                if (nickname != null){
                    actFlowPeopleWhoCopied.setInitiator(nickname);
                }
                List<ActFlowProIdAndNode> actFlowProIdAndNodes = proInsIdAndNodeList.get(actFlowPeopleWhoCopied.getProcessInstanceId());
                if (CollUtil.isNotEmpty(actFlowProIdAndNodes)){
                    ActFlowProIdAndNode actFlowProIdAndNode = actFlowProIdAndNodes.get(0);
                    if (ActFlowActionSettingConstant.REJECT.equals(actFlowProIdAndNode.getDeleteReason())){
                        actFlowPeopleWhoCopied.setStatus(ActFlowStatusConstant.REFUSE);
                    }else if ("endEvent".equals(actFlowProIdAndNode.getActType())) {
                        actFlowPeopleWhoCopied.setStatus(ActFlowStatusConstant.PASS);
                    }else{
                        actFlowPeopleWhoCopied.setStatus(ActFlowStatusConstant.APPROVE);
                        actFlowPeopleWhoCopied.setCurrentApprovalUserList(actFlowProIdAndNodes.stream().filter(item -> "userTask".equals(item.getActType()) && item.getEndTime() == null).map(ActFlowProIdAndNode::getAssignee).collect(Collectors.toList()));
                    }
                }
                //权限处理
                if (processInstanceIdAndFormData != null){
                    Map<String, Object> formData = processInstanceIdAndFormData.get(actFlowPeopleWhoCopied.getProcessInstanceId());
                    FormDataPermissionUtils.processFormDataPermission(formData,formDataPermissionMap.get(actFlowPeopleWhoCopied.getNodeId()));
                    if (queryActFlowPeopleWhoCopied.isNeedFormData()){
                        actFlowPeopleWhoCopied.setFormData(formData);
                    }
                    ActFlowPublishConfigEntity publishConfig;
                    if (deploymentIdAndPublishMap != null && (publishConfig = deploymentIdAndPublishMap.get(actFlowPeopleWhoCopied.getDeploymentId())) != null){
                        actFlowPeopleWhoCopied.setSummary(ELUtils.getSummary(publishConfig.getSummary(),publishConfig.getManyConditionJson(),formData));
                    }
                }

            }
        }
        return pageList;
    }

    private Map<String, String> queryUsernameAndNickname(Set<String> usernames){
        List<ActUser> actUsers = actUserService.queryUserByUsernames(usernames);
        if (CollUtil.isNotEmpty(actUsers)){
            Map<String, String> usernameAndNickname = actUsers.stream().collect(Collectors.toMap(ActUser::getUsername, ActUser::getNickname, (k1, k2) -> k2));
            return usernameAndNickname;
        }
        return new HashMap<>();
    }


    private List<ActFlowProIdAndNode> queryFlowProIdAndNodeGetGroupMax(Set<String> processInstanceIds, String type, boolean unapproved){
        List<ActFlowProIdAndNode> proIdAndNodeList = actExtensionMapper.queryFlowProIdAndNodeByInstanceIds(processInstanceIds, type,unapproved);
        if (CollUtil.isNotEmpty(proIdAndNodeList)){
            proIdAndNodeList  = proIdAndNodeList.stream().filter(item-> !(item.getActId().startsWith(FlowNodeTypeConstant.URGING_NODE) || item.getActId().equals(CommonConstant.URGING_SERVICE_NODE_ID))).collect(Collectors.toList());
            List<ActFlowProIdAndNode> dataList = new ArrayList<>();
            String lastProcessInstanceId = null;
            String lastActId = null;
            for (ActFlowProIdAndNode actFlowProIdAndNode : proIdAndNodeList) {
                if (lastProcessInstanceId == null){
                    lastProcessInstanceId = actFlowProIdAndNode.getProcessInstanceId();
                    lastActId = actFlowProIdAndNode.getActId();
                }
                if (lastProcessInstanceId.equals(actFlowProIdAndNode.getProcessInstanceId())){
                    if (lastActId.equals(actFlowProIdAndNode.getActId())){
                        dataList.add(actFlowProIdAndNode);
                    }
                }else{
                    lastProcessInstanceId = actFlowProIdAndNode.getProcessInstanceId();
                    lastActId = actFlowProIdAndNode.getActId();
                    dataList.add(actFlowProIdAndNode);
                }
            }
            return dataList;
        }
        return proIdAndNodeList;
    }

    private Map<String,Map<String, Object>> queryVariableByExecutionId(Set<String> executionIds){
        if (CollUtil.isEmpty(executionIds)){
            return null;
        }
        List<HistoricVariableInstance> variableList = historyService.createHistoricVariableInstanceQuery().executionIds(executionIds).variableName(CommonConstant.FORM_DATA).list();
        if (CollUtil.isNotEmpty(variableList)){
            return variableList.stream().collect(Collectors.toMap(HistoricVariableInstance::getProcessInstanceId, item -> (Map<String, Object>) item.getValue(), (v1, v2) -> v2));
        }
        return null;
    }

    private Map<String, ActFlowPublishConfigEntity> queryDeploymentIdAndPublishMap(Set<String> deploymentIds){
        List<ActFlowPublishConfigEntity> configurationList = actFlowPublishConfigurationMapper.selectList(new LambdaQueryWrapper<ActFlowPublishConfigEntity>().select(ActFlowPublishConfigEntity::getId, ActFlowPublishConfigEntity::getIconType,ActFlowPublishConfigEntity::getSummary,ActFlowPublishConfigEntity::getManyConditionJson).in(ActFlowPublishConfigEntity::getId, deploymentIds));
        Map<String, ActFlowPublishConfigEntity> deploymentIdAndIcon = configurationList.stream().collect(Collectors.toMap(ActFlowPublishConfigEntity::getId, item->item, (k1, k2) -> k2));
        return deploymentIdAndIcon;
    }
}
